package com.michale.framework.globalException;

import com.michale.common.codemessage.JSRCodeMessage;
import com.michale.common.exception.BaseException;
import com.michale.common.exception.ServiceException;
import com.michale.common.result.AjaxResult;
import com.michale.common.utils.Exception.ExceptionUtils;
import com.michale.common.utils.String.StringUtils;
import io.swagger.annotations.ApiModelProperty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.naming.AuthenticationException;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import static com.michale.common.codemessage.HTTPCodeMessage.*;
import static com.michale.common.utils.Exception.ExceptionUtils.getRootErrorMessage;

/**
 * @author Michale
 * @apiNote 全局异常处理器
 */
@RestControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ApiModelProperty("权限校验异常")
    @ExceptionHandler(AccessDeniedException.class)
    public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址:'{}',权限校验失败:'{}'", requestURI, e.getMessage());
        return AjaxResult.error(UNAUTHORIZED.getCode(), UNAUTHORIZED.getMessage())
                .put(requestURI, e.getMessage());
    }

    @ApiModelProperty("身份验证异常")
    @ExceptionHandler(AuthenticationException.class)
    public AjaxResult handleAuthenticationException(AuthenticationException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址:'{}',身份验证异常:'{}'", requestURI, e.getMessage());
        return AjaxResult.error(AUTHENTICATION_EXCEPTION.getCode(), e.getMessage());
    }

    @ApiModelProperty("请求方式不支持")
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
                                                          HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址:'{}',不支持'{}'请求", requestURI, e.getMethod());
        System.out.println();
        return AjaxResult.error(
                        METHOD_NOT_ALLOWED.getCode(),
                        METHOD_NOT_ALLOWED.getMessage())
                .put("error", "不支持" + e.getMethod() + "请求");
    }

    @ApiModelProperty("请求参数绑定异常")
    @ExceptionHandler(ServletRequestBindingException.class)
    public AjaxResult handleMissingServletRequestParameterException(ServletRequestBindingException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址:'{}',请求参数异常:'{}'.", requestURI, getRootErrorMessage(e));
        return Objects.requireNonNull(AjaxResult.error(
                PARAMETER_BINDING_EXCEPTION.getCode(),
                PARAMETER_BINDING_EXCEPTION.getMessage()
        ).put("path", requestURI)).put("error", getRootErrorMessage(e));
    }


    @ApiModelProperty("参数格式不符合要求")
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public AjaxResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址:'{}',参数格式不符合要求:'{}'.", requestURI, ExceptionUtils.getRootErrorMessage(e));
        String[] s = Objects.requireNonNull(e.getMessage()).split(" ");
        return Objects.requireNonNull(AjaxResult.error(
                PARAMETER_FORMAT_EXCEPTION.getCode(),
                PARAMETER_FORMAT_EXCEPTION.getMessage()
        ).put("path", requestURI)).put("error", "参数类型应该为:" + s[10].split("'")[1]);
    }

    @ApiModelProperty("数据访问异常")
    @ExceptionHandler(DataAccessException.class)
    public AjaxResult handleMysqlDataTruncation(DataAccessException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址:'{}',数据访问异常:'{}'.", requestURI, getRootErrorMessage(e));
        return Objects.requireNonNull(AjaxResult.error(
                DATA_ACCESS_EXCEPTION.getCode(),
                getRootErrorMessage(e)
        ).put("path", requestURI)).put("error", getRootErrorMessage(e));
    }

    @ApiModelProperty("自定义校验异常")
    @ExceptionHandler(BindException.class)
    public AjaxResult handleValidException(BindException e, HttpServletRequest request) {
        log.error("数据校验出现问题:{},异常类型:{}", e.getMessage(), e.getClass());
        BindingResult bindingResult = e.getBindingResult();
        String requestURI = request.getRequestURI();
        Map<String, String> errorMap = new HashMap<>();
        bindingResult.getFieldErrors().forEach((fieldError) -> errorMap.put(fieldError.getField(), fieldError.getDefaultMessage()));
        return Objects.requireNonNull(AjaxResult.error(
                JSRCodeMessage.CHECK_PARAMETERS_ERROR.getCode(),
                JSRCodeMessage.CHECK_PARAMETERS_ERROR.getMessage()
        ).put("path", requestURI)).put("errors", errorMap);
    }

    @ApiModelProperty("自定义业务异常")
    @ExceptionHandler(ServiceException.class)
    public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址:'{}',业务名称:'{}',自定义业务异常:'{}'.", requestURI, e.getName(), e.getClass());
        Integer code = e.getCode();
        if (StringUtils.isNotNull(code)) {
            return AjaxResult.error(code, e.getMessage()).put("name", e.getName());
        }
        return AjaxResult.error(SERVICE_EXCEPTION.getCode(), e.getMessage()).put("name", e.getName());
    }

    @ApiModelProperty("基础异常")
    @ExceptionHandler(BaseException.class)
    public AjaxResult handleBaseException(BaseException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址:'{}',所属模块:'{}',基础异常:'{}'.", requestURI, e.getModule(), e.getClass());
        return AjaxResult.error(BASE_EXCEPTION.getCode(), e.getMessage());
    }


    @ApiModelProperty("未知的运行时异常")
    @ExceptionHandler(RuntimeException.class)
    public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址:'{}',拦截未知的运行时异常:'{}'.", requestURI, e);
        return AjaxResult.error(
                SYSTEM_RUN_EXCEPTION.getCode(), getRootErrorMessage(e)).put("ExceptionName", e.getClass());
    }

    @ApiModelProperty("系统未知异常")
    @ExceptionHandler(Exception.class)
    public AjaxResult handleException(Exception e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址:'{}',系统未知异常:'{}'.", requestURI, e);
        return Objects.requireNonNull(Objects.requireNonNull(AjaxResult.error(
                SYSTEM_EXCEPTION.getCode(),
                SYSTEM_EXCEPTION.getMessage()
        ).put("ExceptionName", e.getClass())).put("path", requestURI)).put("error", getRootErrorMessage(e));
    }

}
